import { Controller, Headers, Get, Post, Param, Body, UsePipes, ValidationPipe, UploadedFile, UseInterceptors, Query, Put, Delete } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiQuery } from '@nestjs/swagger';
import { Injectable, Inject, forwardRef, BadRequestException, InternalServerErrorException } from '@nestjs/common';
import { TransformInterceptor } from 'src/interceptor/transform.interceptor';
import { FileInterceptor } from '@nestjs/platform-express';
import { MinioClient } from '../app.minio';
import { TransDto } from './ffmpeg.dto';
import * as uuid from 'uuid';
const Fs = require('fs')
const ffmpeg = require('../utils/ffmpeg');
const path = require('path')
const exec = require('child_process').exec
@ApiTags('音视频处理')
@Controller('ffmpeg')
@UseInterceptors(TransformInterceptor)
export class FfmpegController {
    @Post('uploadTrans')
    @ApiOperation({ summary: '上传一个对象并转码' })
    @UseInterceptors(FileInterceptor('file'))
    async uploadTrans(@UploadedFile() file: Express.Multer.File, @Body() body) {
        return await new Promise(resolve => {
            try {
                const fileGuid = uuid.v4()
                const [key, objectName] = fileName(body.objectName)
                mkdirSync(path.join(__dirname, `../../public/cache/${fileGuid}/`))
                mkdirSync(path.join(__dirname, `../../public/${fileGuid}/`))
                Fs.writeFileSync(path.join(__dirname, `../../public/cache/${fileGuid}/${fileGuid}.mp4`), file.buffer);
                if (body.type == 'hls') {
                    const command = new ffmpeg(path.join(__dirname, `../../public/cache/${fileGuid}/${fileGuid}.mp4`))
                    command.videoCodec('libx264') // 设置视频编解码器
                        // .audioCodec('libfaac') // 设置 音频解码器
                        .format('hls') // 输出视频格式
                        .outputOptions('-hls_list_size 0') //  -hls_list_size n:设置播放列表保存的最多条目，设置为0会保存有所片信息，默认值为5
                        .outputOption('-hls_time 10') // -hls_time n: 设置每片的长度，默认值为2。单位为秒
                        .output(path.join(__dirname, `../../public/${fileGuid}/${objectName.replace('.mp4', '.m3u8')}`)) // 输出文件
                        .on('progress', (progress) => { // 监听切片进度
                            console.log(progress)
                            // console.log('Processing: ' + progress.percent + '% done');
                        })
                        .on('end', () => {
                            return Fs.readdir(path.join(__dirname, `../../public/${fileGuid}/`), async (err, data) => {
                                if (err) throw new BadRequestException(err);
                                let a = []
                                for (let item of data) {
                                    a.push(new MinioClient().putObject2(path.join(__dirname, `../../public/${fileGuid}/`) + item, { bucketName: body.bucketName, objectName: key + item }))
                                }
                                await Promise.all(a)
                                await removePromise(path.join(__dirname, `../../public/cache/${fileGuid}/`))
                                await removePromise(path.join(__dirname, `../../public/${fileGuid}/`))
                                resolve(body.objectName.replace('.mp4', '.m3u8'))
                            })
                        })
                        .run();
                }


            } catch (error) {
                throw new BadRequestException(error);
            }
        })

    }
    @Post('trans')
    @ApiOperation({ summary: '转码' })
    async trans(@Body() body: TransDto) {
        return await new Promise(async resolve => {
            try {
                let content = new Buffer(0);
                const readerStream = await new MinioClient().getObject(body.bucketName, body.objectName);
                readerStream.on('data', chunk => {
                    content = Buffer.concat([content, chunk]);
                });
                readerStream.on('error', function (err) {
                    console.log(err.stack);
                    throw new BadRequestException(err.stack);
                })
                readerStream.on('end', () => {
                    const fileGuid = uuid.v4()
                    const [key, objectName] = fileName(body.objectName)
                    mkdirSync(path.join(__dirname, `../../public/cache/${fileGuid}/`))
                    mkdirSync(path.join(__dirname, `../../public/${fileGuid}/`))
                    Fs.writeFileSync(path.join(__dirname, `../../public/cache/${fileGuid}/${fileGuid}.mp4`), content);
                    if (body.type == 'hls') {
                        const command = new ffmpeg(path.join(__dirname, `../../public/cache/${fileGuid}/${fileGuid}.mp4`))
                        command.videoCodec('libx264') // 设置视频编解码器
                            // .audioCodec('libfaac') // 设置 音频解码器
                            .format('hls') // 输出视频格式
                            .outputOptions('-hls_list_size 0') //  -hls_list_size n:设置播放列表保存的最多条目，设置为0会保存有所片信息，默认值为5
                            .outputOption('-hls_time 10') // -hls_time n: 设置每片的长度，默认值为2。单位为秒
                            .output(path.join(__dirname, `../../public/${fileGuid}/${objectName}.m3u8`)) // 输出文件
                            .on('progress', (progress) => { // 监听切片进度
                                console.log('Processing: ' + progress.percent + '% done');
                            })
                            .on('end', () => {
                                return Fs.readdir(path.join(__dirname, `../../public/${fileGuid}/`), async (err, data) => {
                                    if (err) throw new BadRequestException(err);
                                    let a = []
                                    for (let item of data) {
                                        a.push(new MinioClient().putObject2(path.join(__dirname, `../../public/${fileGuid}/`) + item, { bucketName: body.bucketName, objectName: key + item }))
                                    }
                                    await Promise.all(a)
                                    await removePromise(path.join(__dirname, `../../public/cache/${fileGuid}/`))
                                    await removePromise(path.join(__dirname, `../../public/${fileGuid}/`))
                                    resolve(body.objectName + '.m3u8')
                                })
                            })
                            .run();
                    }
                });
            } catch (error) {
                throw new BadRequestException(error);
            }
        })

    }
    @Get('vframe')
    @ApiOperation({ summary: '获取视频某帧的图片' })
    async getObject(@Query('bucketName') bucketName: string, @Query('objectName') objectName: string) {
        return new Promise((resolve, reject) => {
            let minioClient = new MinioClient()
            let url = minioClient.getObjectUrl(bucketName, objectName)
            let reg = /\.\w+$/;
            let guid = uuid.v4()
            let file_name_path = objectName.replace(reg, '.png');
            new ffmpeg({ source: url })
                .on('end', function () {
                    minioClient.putObject2(path.join(__dirname, `../../public/cache/${guid}.png`), {
                        objectName: file_name_path, bucketName
                    }).then(res => {
                        Fs.unlinkSync(path.join(__dirname, `../../public/cache/${guid}.png`))
                        resolve({ key: res })
                    });
                })
                .on('error', function (err, stdout, stderr) {
                    console.log('Cannot process video: ' + err.message);
                    reject(err)
                })
                .screenshots({
                    timestamps: ['1%'],
                    filename: guid + '.png',
                    folder: path.join(__dirname, `../../public/cache/`)
                });
        }).catch(err => {
            throw new BadRequestException(err);
        })

    }
}

function mkdirSync(path) {
    if (!Fs.existsSync(path)) {
        Fs.mkdirSync(path)
    }
}
//解析文件目录和文件名
function fileName(file) {
    let a = file.split('/'), key = ''
    key = a.pop()
    return [a.join('/') + '/', key]
}
//删除文件夹
function removePromise(dir) {
    return new Promise(function (resolve, reject) {
        //先读文件夹
        Fs.stat(dir, function (err, stat) {
            if (stat.isDirectory()) {
                Fs.readdir(dir, function (err, files) {
                    files = files.map(file => path.join(dir, file)); // a/b  a/m
                    files = files.map(file => removePromise(file)); //这时候变成了promise
                    Promise.all(files).then(function () {
                        Fs.rmdir(dir, resolve);
                    })
                })
            } else {
                Fs.unlink(dir, resolve)
            }
        })

    })
}